To upload the data, download the uber_pickups_lower_manhattan_wide_6h.csv and stick it in a folder and remember the directory.
# Install required libraries if necessary.
require("forecast")
require("lubridate")
Loading required package: lubridate
Attaching package: ‘lubridate’
The following object is masked from ‘package:base’:
date
require("gridExtra")
require("tidyverse")
Loading required package: tidyverse
[37m── [1mAttaching packages[22m ──────────────────────── tidyverse 1.2.1 ──[39m
[37m[32m✔[37m [34mtibble [37m 2.1.3 [32m✔[37m [34mpurrr [37m 0.3.2
[32m✔[37m [34mtidyr [37m 1.0.0 [32m✔[37m [34mdplyr [37m 0.8.3
[32m✔[37m [34mreadr [37m 1.3.1 [32m✔[37m [34mstringr[37m 1.4.0
[32m✔[37m [34mtibble [37m 2.1.3 [32m✔[37m [34mforcats[37m 0.4.0[39m
[37m── [1mConflicts[22m ─────────────────────────── tidyverse_conflicts() ──
[31m✖[37m [34mlubridate[37m::[32mas.difftime()[37m masks [34mbase[37m::as.difftime()
[31m✖[37m [34mdplyr[37m::[32mcombine()[37m masks [34mgridExtra[37m::combine()
[31m✖[37m [34mlubridate[37m::[32mdate()[37m masks [34mbase[37m::date()
[31m✖[37m [34mdplyr[37m::[32mfilter()[37m masks [34mtimeSeries[37m::filter(), [34mstats[37m::filter()
[31m✖[37m [34mlubridate[37m::[32mintersect()[37m masks [34mbase[37m::intersect()
[31m✖[37m [34mdplyr[37m::[32mlag()[37m masks [34mtimeSeries[37m::lag(), [34mstats[37m::lag()
[31m✖[37m [34mlubridate[37m::[32msetdiff()[37m masks [34mbase[37m::setdiff()
[31m✖[37m [34mlubridate[37m::[32munion()[37m masks [34mbase[37m::union()[39m
require("caret")
Loading required package: caret
Loading required package: lattice
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Attaching package: ‘caret’
The following object is masked from ‘package:purrr’:
lift
require("tscount")
Loading required package: tscount
# Input data
uber_6h <- read_csv('../data/uber_pickups_lower_manhattan_wide_6h.csv') # Point this to the directory and file
Parsed with column specification:
cols(
Pickup_date = [34mcol_datetime(format = "")[39m,
East_Village = [32mcol_double()[39m,
Gramercy = [32mcol_double()[39m,
GVillage_N = [32mcol_double()[39m,
GVillage_S = [32mcol_double()[39m,
Little_Italy = [32mcol_double()[39m,
LES = [32mcol_double()[39m,
SoHo = [32mcol_double()[39m,
Union_Sq = [32mcol_double()[39m
)
uber_train <- uber_6h %>% filter(Pickup_date < ymd_hms("2015-06-01 00:00:00")) # This gives us a training set for all 8 locations
uber_test <- uber_6h %>% filter(Pickup_date >= ymd_hms("2015-06-01 00:00:00"))
uber_train <- uber_train %>% mutate(season=weekdays(Pickup_date, abbreviate=TRUE))
uber_test <- uber_test %>% mutate(season=weekdays(Pickup_date, abbreviate=TRUE))
# nrow(uber_6h) # if we want to check number of observations
# names(uber_6h) # use this command if you want to see the available time series
Train-test split
We will use the first five months as training data and predict Uber pickups for June 2015 in the East Village. The date is in positx format which can be parsed using the lubridate package.
Statistical forecasting techniques are used on the East Village training data, and will be evaluated on East Village test data.
# This block selects only east village data and splits it into train/test sets
full_ts <- msts(uber_6h$East_Village,
start=decimal_date(ymd_hms("2015-01-01 00:00:00")),
seasonal.periods=c(4, 1461))
train_ts <- msts(uber_train$East_Village,
start=decimal_date(ymd_hms("2015-01-01 00:00:00")),
seasonal.periods=c(4, 1461))
test_ts <- msts(uber_test$East_Village,
start=decimal_date(ymd_hms("2015-06-01 00:00:00")),
seasonal.periods=c(4, 1461))
#train_ts <- window(full_ts, start=decimal_date(ymd_hms("2015-01-01 00:00:00"))) # Train with obsercations up until june
#test_ts <- window(full_ts, start=decimal_date(ymd_hms("2015-06-01 00:00:00"))) # Test out predictions with June data
y_test <- as.numeric(test_ts)
test_size <- length(y_test)
# Show full data set
p1 <- full_ts %>% autoplot(series="All Trips") +
ggtitle("Full Dataset") +
guides(colour=guide_legend("Data"))+
scale_color_manual(values=c("black"))
# Show training dataset
p2 <- autoplot(train_ts, series="Training")+
autolayer(test_ts, series="Validation")+
guides(colour=guide_legend("Split"))+
scale_color_manual(values=c("black", "grey"))+
ggtitle("Train/Test Split")
grid.arrange(p1, p2, nrow=2, ncol=1)

To look at any of the other 7 pickup locations, call lil_italy <- uber_train %>% select(Little_Italy) for example.
# Show subset of data to look for patterns
jan_to_march <- train_ts %>% window(end=decimal_date(ymd_hms("2015-03-01 00:00:00")))
jan_to_march %>% autoplot()+
ggtitle("2 Months of Uber Pickup Data")

By aggregating to 6-hour windows we model the demand per “shift” in a day.
- 00:00 - 05:59 - Graveyard Shift
- 06:00 - 11:59 - Morning Shift
- 12:00 - 17:59 - Afternoon Shift
- 18:00 - 23:59 - Evening Shift
Moving Average Filter

This helps with telling the seasonality of the data. It looks like there is a spike in demand at the beginning of each month. In addition there are about three spikes during each month. This probably corresponds to weekly spikes in demand.
Analyzing the trend
lm(train_ts ~ time(train_ts)) %>% fitted() -> yhat.lm
autoplot(train_ts) +
geom_line(mapping=aes(x=time(train_ts), y=yhat.lm), color="red")+
ggtitle("Linear Trend Fit")+
xlab("Time")+
ylab("Pickups")

There is a slight linear trend. Let’s see what the ACF and PACF plots look like.
p1 <- ggAcf(train_ts) + ggtitle("ACF of Uber Pickups")
p2 <- ggPacf(train_ts) + ggtitle("PACF of Uber Pickups")
grid.arrange(p1, p2, nrow=2, ncol=1)

Not very informative although it initially hints toward an AR(p) model after a difference.
Before checking the lag plot, the trend should be removed via differencing.
Differencing
# code for differencing the training data
train_diff <- diff(train_ts)
lm(train_diff ~ time(train_diff)) %>% fitted() -> yhat.lm
train_diff %>% autoplot() +
geom_line(mapping=aes(x=time(train_diff), y=yhat.lm), color="red") +
ggtitle("Differenced Pickup Data") +
ylab("Change in Pickups")

# Comparison of time series plots
lm(train_ts ~ time(train_ts)) %>% fitted() -> yhat.lm1
p1 <- autoplot(train_ts) +
geom_line(mapping=aes(x=time(train_ts), y=yhat.lm1), color="red")+
ggtitle("Linear Trend Fit")+
xlab("Time")+
ylab("Pickups")
# code for differencing the training data
train_diff <- diff(train_ts)
lm(train_diff ~ time(train_diff)) %>% fitted() -> yhat.lm2
p2 <- train_diff %>% autoplot() +
geom_line(mapping=aes(x=time(train_diff), y=yhat.lm2), color="red") +
ggtitle("Differenced Pickup Data") +
ylab("Change in Pickups")
grid.arrange(p1, p2, nrow=1, ncol=2)

Zoom in
diffwin <- train_diff %>% window(end=decimal_date(ymd_hms("2015-03-01 00:00:00")))
diffwin %>% autoplot() + ggtitle("2 Months of Differenced Pickups")

diffwin %>% autoplot(series="Original Data")+
autolayer(ma(diffwin, 4), series="Moving Average")+
jan + feb + mar +
guides(colour=guide_legend("Split"))+
scale_color_manual(values=c("black", "grey"))+
ggtitle("Moving Average of Training Data (Jan and Feb)")
p1 <- train_diff %>% ggAcf() + ggtitle("ACF of Differenced Uber Pickups")
p2 <- train_diff %>% ggPacf() + ggtitle("PACF of Differenced Uber Pickups")
grid.arrange(p1, p2, nrow=1, ncol=2)
train_diff %>% Acf(plot=FALSE, lag.max=30)
Autocorrelations of series ‘.’, by lag
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1.000 -0.387 -0.168 -0.189 0.598 -0.307 -0.039 -0.118 0.274 -0.157 0.015 -0.129 0.233 -0.135 0.031 -0.125 0.220 -0.130 0.021 -0.149 0.260
21 22 23 24 25 26 27 28 29 30
-0.117 -0.025 -0.291 0.558 -0.179 -0.145 -0.343 0.855 -0.337 -0.145
# PACF of differenced data
train_diff %>% Pacf(plot=FALSE, lag.max=30)
Partial autocorrelations of series ‘.’, by lag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
-0.387 -0.373 -0.577 0.246 -0.030 0.088 0.010 -0.163 -0.065 -0.090 -0.164 0.078 -0.058 0.005 -0.067 -0.042 -0.023 -0.068 -0.192 0.003 -0.031
22 23 24 25 26 27 28 29 30
-0.066 -0.491 0.181 0.062 -0.101 -0.448 0.428 0.114 0.145
There is clear seasonality and a sharp cutoff after lag 26-ish. Each lag is 1 shift, so 24 shifts would be 6 days of pickups, 26 shifts would be 6.5 days of pickups (evening shift of the 6th day).
In the ACF plot there is a spike at lag 4, and another lower spike at lag 8, lag 12, etc. In addition, there are large spikes at lag 28n. This indicates an AR(4) component and seasonal component at lag 28. The PACF spikes and cuts off after lag 3, and spikes again at lag 28. This further suggests an AR(4) or AR(3) model.

We looked at the lags for the differenced time series. We suspect that there is a positive autocorrelation at lag 4n because of assumed daily seasonality. The other place to check would be where the ACF’s seasonal decay exhibited spikes at lag 28n. Looking at the plot above it’s clear that this is a highly predictive lag.
Next we difference the series S=28 times for the D=1 seasonal difference. Here we can see what the order of the seasonal component would be.
train_sdiff <- train_ts %>% diff() %>% diff(lag=28)
p1 <- train_sdiff %>% ggAcf() + ggtitle("ACF of Diff(28) data")
p2 <- train_sdiff %>% ggPacf() + ggtitle("PACF of Diff(28) data")
grid.arrange(p1, p2, nrow=2, ncol=1)

# ACF of seasonal component
train_ts %>% diff() %>% diff(lag=28) %>% Acf(plot=FALSE, lag.max=30)
Autocorrelations of series ‘.’, by lag
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1.000 -0.275 -0.206 -0.063 0.213 -0.073 -0.069 0.035 -0.008 0.006 0.002 0.010 0.014 -0.064 0.041 -0.018 0.013 -0.022 0.010 -0.026 0.042
21 22 23 24 25 26 27 28 29 30
-0.038 0.066 0.021 -0.082 -0.017 0.138 0.087 -0.406 0.078 0.148
# PACF of seasonal component
train_ts %>% diff() %>% diff(lag=28) %>% Pacf(plot=FALSE, lag.max=30)
Partial autocorrelations of series ‘.’, by lag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
-0.275 -0.305 -0.264 0.038 -0.059 -0.058 0.005 -0.070 -0.015 0.000 -0.004 0.040 -0.056 0.005 -0.035 -0.019 -0.012 -0.021 -0.046 0.016 -0.049
22 23 24 25 26 27 28 29 30
0.064 0.082 -0.039 -0.015 0.103 0.189 -0.298 -0.129 -0.075
The ACF and PACF of the seasonal differenced data look very similar. There is a spike at lag 1 and at lag 28 (ACF) and lag 29 (PACF). Besides these two spikes, the rest of the values tend to stick around zero. This is indicative of an SMA(1) seasonal component. There doesn’t appear to be any SAR(P) component. Since we took one seasonal difference, then D=1.
We will now make an ARIMA model based on the ACF and PACF plots of the diff(1) and diff(28) version of our original data.
SARIMA Models
The first model is based on the model presented in the time series analysis book mixed with the seasonality that we observe in the Uber dataset. This is a model that is used in economics and was used to predict airline passengers in the book.
# Intuitive model based on the plots
m1 <- Arima(train_ts,
order=c(1, 1, 0),
seasonal=list(order=c(0,1,1), period=28))
yhat <- m1 %>% forecast(h=test_size)
# Plot the predictions
p1 <- yhat %>% autoplot()
p2 <- ggAcf(residuals(m1)) + ggtitle("ACF of m1 Residuals")
grid.arrange(p1, p2, nrow=1, ncol=2)

# Print out summary of the coefficients
m1 %>% summary()
Series: train_ts
ARIMA(1,1,0)(0,1,1)[28]
Coefficients:
ar1 sma1
-0.3765 -0.9039
s.e. 0.0426 0.0498
sigma^2 estimated as 26438: log likelihood=-3766.04
AIC=7538.09 AICc=7538.13 BIC=7551.15
Training set error measures:
ME RMSE MAE MPE MAPE MASE ACF1
Training set 7.162789 158.37 98.86515 Inf Inf NaN -0.06150298
The pattern looks like it was captured in the predictive mean of the forecast, but the prediction intervals get really wide for forecasts beyond about 2 weeks. At this point, the prediction interval captures negative values.
Next we use p=4 since this is what was uncovered in the ACF and PACF plots of the differenced time series.
m2 <- Arima(train_ts,
order=c(4, 1, 0),
seasonal=list(order=c(0,1,1), period=28))
yhat2 <- m2 %>% forecast(h=test_size)
p1 <- yhat2 %>% autoplot()
p2 <- ggAcf(residuals(m2)) + ggtitle("ACF of Residuals")
grid.arrange(p1, p2, nrow=1, ncol=2)

This second model dips below zero in the prediction interval after the first week. The AIC and BIC of this model are also lower than that of the first economic model.
The last model is one that is chosen by auto.arima on the basis of AIC. However, the function needs a time series with frequency defined and I struggled to understand the frequency parameter. I made a guess that it would be the number of days in a year (1 season) divided by the value of the season present in my data.
# Auto arima with forced seasonality
train_seasonal <- ts(uber_train$East_Village, frequency=365.4/28)
m_auto <- auto.arima(train_seasonal, D=1)
m_auto %>% forecast(h=test_size) %>% autoplot()

m_auto_y <- m_auto %>% forecast(h=test_size)
m_auto <- Arima(train_ts,
order=c(3, 0, 2),
seasonal=list(order=c(2,1,0), period=13))
m_auto_y <- m_auto %>% forecast(h=test_size)

This model does worse than the first two.
# Comparison of model results
p1 <- ggplot()+
geom_point(mapping=aes(x=y_test, y=yhat$mean))+
xlim(min(y_test), max(y_test))+
ylim(min(y_test), max(y_test))+
ggtitle("Actual vs. Predicted (m1)")
p2 <- ggplot()+
geom_point(mapping=aes(x=y_test, y=yhat2$mean))+
xlim(min(y_test), max(y_test))+
ylim(min(y_test), max(y_test))+
ggtitle("Actual vs. Predicted (m2)")
grid.arrange(p1, p2, nrow=1, ncol=2)

m7 <- Arima(train_ts,
order=c(4, 1, 0),
seasonal=list(order=c(0,1,4), period=28))
yhat7 <- m7 %>% forecast(h=test_size)
p1 <- yhat7 %>% autoplot()
p2 <- ggAcf(residuals(m7)) + ggtitle("ACF of Residuals")
grid.arrange(p1, p2, nrow=1, ncol=2)

BIC(m7)
[1] 7460.809
m8 <- Arima(train_ts,
order=c(3, 1, 0),
seasonal=list(order=c(0,1,1), period=28))
yhat8 <- m8 %>% forecast(h=test_size)
p1 <- yhat8 %>% autoplot()
p2 <- ggAcf(residuals(m8)) + ggtitle("ACF of Residuals")
grid.arrange(p1, p2, nrow=1, ncol=2)

Ljung-Box Tests
# Ljung Box tests
fit_test <- sarima(train_ts, p=4, d=1, q=0, P=0, D=1, Q=1, S=28)
fit_test
m1 was pretty good but didn’t pass the Ljung-Box test of significance. However m2 did. And this also assumes normally-distributed errors which isn’t exactly true with our data since it’s Poisson distributed.
Poisson Models
# One-hot encoding function for weekly seasonal variable
dmy <- dummyVars(~season, data = uber_train)
trainCovariates <- data.frame(predict(dmy ,newdata=data.frame(season=uber_train$season)))
testCovariates <- data.frame(predict(dmy ,newdata=data.frame(season=uber_test$season)))
#
m3 <- tsglm(train_ts, model=list(past_obs=1, past_mean=1), distr="poisson", xreg=trainCovariates)
At least one estimated parameter corresponding to a covariate is very close to
zero, the boundary of the parameter constraint for this parameter. This might
be an appropriate estimation but could also indicate that the parameter is
actually negative and the chosen model is too restrictive. In such a case one
could consider using a model with the logarithmic link function. Please check
results carefully.
yhat_m3 <- predict(m3, n.ahead=test_size, newxreg=testCovariates)
predint_m3 <- data.frame(yhat_m3$interval)
ggplot()+
geom_line(mapping=aes(x=time(train_ts), y=train_ts))+
geom_ribbon(mapping=aes(x=time(test_ts), ymin=predint_m3$lower, ymax = predint_m3$upper), fill="blue", alpha=0.5)+
geom_line(mapping=aes(x=time(test_ts), y=yhat_m3$median), color="blue")+
ggtitle("Poisson GLM with ARMA(1,1) and Weekday Dummy Variable")

m4 <- tsglm(train_ts, model=list(past_obs=1, past_mean=1), distr="poisson")
yhat_m4 <- predict(m4, n.ahead=test_size)
predint_m4 <- data.frame(yhat_m4$interval)
ggplot()+
geom_line(mapping=aes(x=time(train_ts), y=train_ts))+
geom_ribbon(mapping=aes(x=time(test_ts), ymin=predint_m4$lower, ymax = predint_m4$upper), fill="blue", alpha=0.5)+
geom_line(mapping=aes(x=time(test_ts), y=yhat_m4$median), color="blue")+
ggtitle("Poisson GLM with ARMA(1,1)")

mean((y_test - yhat_m6$median)^2)
[1] 29587.04
dmy <- dummyVars(~season, data = uber_train)
trainCovariates <- data.frame(predict(dmy ,newdata=data.frame(season=uber_train$season)))
testCovariates <- data.frame(predict(dmy ,newdata=data.frame(season=uber_test$season)))
m6 <- tsglm(train_ts, model=list(past_obs=4, past_mean=1), distr="poisson", xreg=trainCovariates)
predint_m6 <- data.frame(yhat_m6$interval)
yhat_m6 <- predict(m6, n.ahead=test_size, newxreg=testCovariates)
ggplot()+
geom_line(mapping=aes(x=time(train_ts), y=train_ts))+
geom_ribbon(mapping=aes(x=time(test_ts), ymin=predint_m6$lower, ymax = predint_m6$upper), fill="blue", alpha=0.5)+
geom_line(mapping=aes(x=time(test_ts), y=yhat_m6$median), color="blue")+
ggtitle("Poisson GLM ARMA(4,1) and Weekday Dummy Variable")

That works, should I include the AIC / BIC tables up here too? And the residuals plots and stuff? Alrighty, if you could just put an example section with your model and I can just copy the same format. I have plenty of BS I can stick in d:
Jk im gonna delete this so we have space haha but I’ll copy it in a separate texct file Could you include a quick writeup on it? I was gonna write up something quick about SARIMA, and William is gonna write up something about Poisson. Sounds good, would you be down to put it up here? We were trying to think of what to put up in this section (unless you have an idea of what to put . Sounds good
Our analysis considered three models:\ thats right, and multivariate generalized additive models, or VGAM’s.Sure, ill do a write up in the model fitting section, ill type out the actual models mathematically and try to explain them. yea i can write it here instead, for model fitting and validation put plots of your forecast beside the validation and talk about how well they fit the data. also probably include tables with the models and errors on the validation set. Yes, and the ljung box stuff too, the procedures you used to choose the best model should be there like the aic, but lets not go too crazy lol. I think at most we include validation error, aic, residual or Q-Q plots, and ljung box should be good. for sure, alright have fun lol
train_freq <- ts(uber_train$East_Village, frequency=28)
test_freq <- ts(uber_test$East_Village, frequency=28)
train_freq %>% auto.arima() -> m10
p1 <- m10 %>% forecast(h=120) %>% autoplot()
p2 <- m10 %>% residuals() %>% ggAcf() + ggtitle("ACF of Residuals")
grid.arrange(p1, p2, nrow=1, ncol=2)
checkresiduals(m10)
Ljung-Box test
data: Residuals from ARIMA(4,0,0)(2,1,0)[28]
Q* = 24.923, df = 50, p-value = 0.9989
Model df: 6. Total lags used: 56

naives_m <- Arima(train_ts,
order=c(0, 0, 0),
seasonal=list(order=c(0,1,0), period=28))
sfit <- train_ts %>% snaive(m=28)
p1 <- naives_m %>% forecast(h=test_size) %>% autoplot(PI=FALSE)
p2 <- sfit %>% forecast(h=test_size) %>% autoplot()
grid.arrange(p1, p2, nrow=2, ncol=1)

sfit <- snaive(train_ts)
yhat_sfit <- sfit %>% forecast(h=test_size)
sfit %>% forecast(h=test_size) %>% autoplot()
Accuracy Measures
# SARIMA models
m1 <- Arima(train_ts,
order=c(1, 1, 0),
seasonal=list(order=c(0,1,1), period=28))
m2 <- Arima(train_ts,
order=c(4, 1, 0),
seasonal=list(order=c(0,1,1), period=28))
m3 <- Arima(train_ts,
order=c(4, 1, 0),
seasonal=list(order=c(0,1,4), period=28))
m4 <- Arima(train_ts,
order=c(3, 0, 2),
seasonal=list(order=c(2,1,0), period=13))
m5 <- Arima(train_ts,
order=c(4, 0, 0),
seasonal=list(order=c(2,1,0), period=28))
accuracy(yhat_m1, test_ts)
ME RMSE MAE MPE MAPE MASE ACF1 Theil's U
Training set 7.162789 158.3700 98.86515 Inf Inf NaN -0.06150298 NA
Test set -183.437561 494.5975 411.62220 -67.0349 88.51759 NaN -0.34752397 0.9912245
accuracy(yhat_m2, test_ts)
ME RMSE MAE MPE MAPE MASE ACF1 Theil's U
Training set 7.269319 142.9743 92.78463 -Inf Inf NaN 0.0640973 NA
Test set -110.032570 471.5929 388.80887 -51.40147 79.25787 NaN -0.3495840 0.9525737
accuracy(yhat_m3, test_ts)
ME RMSE MAE MPE MAPE MASE ACF1 Theil's U
Training set 7.317047 138.2777 90.03849 -Inf Inf NaN 0.06230789 NA
Test set -90.257804 461.6869 380.91922 -47.90756 76.96151 NaN -0.34702807 0.920203
accuracy(yhat_m4, test_ts)
ME RMSE MAE MPE MAPE MASE ACF1 Theil's U
Training set 5.363351 331.8169 243.259 -Inf Inf NaN -0.0003350836 NA
Test set -1.991453 409.3317 326.022 -31.38139 65.15798 NaN -0.0211198402 0.7923496
accuracy(yhat_m5, test_ts)
ME RMSE MAE MPE MAPE MASE ACF1 Theil's U
Training set 6.122341 146.6422 89.46945 -Inf Inf NaN 0.007535442 NA
Test set 20.745266 446.7924 362.64753 -24.49447 65.80993 NaN -0.325192843 0.9155374
accuracy(yhat_naive, test_ts)
ME RMSE MAE MPE MAPE MASE ACF1 Theil's U
Training set 9.250045 197.3485 112.4388 -Inf Inf NaN 0.3886022 NA
Test set 47.924370 445.6627 359.8908 -16.1501 63.4041 NaN -0.3310975 0.9683322
mae_tsglm28
[1] 90.35833
LS0tCnRpdGxlOiAiVWJlciBUcmlwcyBBbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKClRvIHVwbG9hZCB0aGUgZGF0YSwgZG93bmxvYWQgdGhlIGB1YmVyX3BpY2t1cHNfbG93ZXJfbWFuaGF0dGFuX3dpZGVfNmguY3N2YCBhbmQgc3RpY2sgaXQgaW4gYSBmb2xkZXIgYW5kIHJlbWVtYmVyIHRoZSBkaXJlY3RvcnkuIAoKYGBge3J9CiMgSW5zdGFsbCByZXF1aXJlZCBsaWJyYXJpZXMgaWYgbmVjZXNzYXJ5LgpyZXF1aXJlKCJmb3JlY2FzdCIpCnJlcXVpcmUoImx1YnJpZGF0ZSIpCnJlcXVpcmUoImdyaWRFeHRyYSIpCnJlcXVpcmUoInRpZHl2ZXJzZSIpCnJlcXVpcmUoImNhcmV0IikKcmVxdWlyZSgidHNjb3VudCIpCmBgYAoKYGBge3J9CiMgSW5wdXQgZGF0YQp1YmVyXzZoIDwtIHJlYWRfY3N2KCcuLi9kYXRhL3ViZXJfcGlja3Vwc19sb3dlcl9tYW5oYXR0YW5fd2lkZV82aC5jc3YnKSAjIFBvaW50IHRoaXMgdG8gdGhlIGRpcmVjdG9yeSBhbmQgZmlsZQoKdWJlcl90cmFpbiA8LSB1YmVyXzZoICU+JSBmaWx0ZXIoUGlja3VwX2RhdGUgPCB5bWRfaG1zKCIyMDE1LTA2LTAxIDAwOjAwOjAwIikpICMgVGhpcyBnaXZlcyB1cyBhIHRyYWluaW5nIHNldCBmb3IgYWxsIDggbG9jYXRpb25zCnViZXJfdGVzdCA8LSB1YmVyXzZoICU+JSBmaWx0ZXIoUGlja3VwX2RhdGUgPj0geW1kX2htcygiMjAxNS0wNi0wMSAwMDowMDowMCIpKQp1YmVyX3RyYWluIDwtIHViZXJfdHJhaW4gJT4lIG11dGF0ZShzZWFzb249d2Vla2RheXMoUGlja3VwX2RhdGUsIGFiYnJldmlhdGU9VFJVRSkpCnViZXJfdGVzdCA8LSB1YmVyX3Rlc3QgJT4lIG11dGF0ZShzZWFzb249d2Vla2RheXMoUGlja3VwX2RhdGUsIGFiYnJldmlhdGU9VFJVRSkpCiMgbnJvdyh1YmVyXzZoKSAjIGlmIHdlIHdhbnQgdG8gY2hlY2sgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwojIG5hbWVzKHViZXJfNmgpICMgdXNlIHRoaXMgY29tbWFuZCBpZiB5b3Ugd2FudCB0byBzZWUgdGhlIGF2YWlsYWJsZSB0aW1lIHNlcmllcwp0cmFpbl9mcmVxIDwtIHRzKHViZXJfdHJhaW4kRWFzdF9WaWxsYWdlLCBmcmVxdWVuY3k9MjgpCnRlc3RfZnJlcSA8LSB0cyh1YmVyX3Rlc3QkRWFzdF9WaWxsYWdlLCBmcmVxdWVuY3k9MjgpCmBgYAoKYGBge3J9CmhlYWQodWJlcl90ZXN0KQpgYGAKCgojIyBUcmFpbi10ZXN0IHNwbGl0CgpXZSB3aWxsIHVzZSB0aGUgZmlyc3QgZml2ZSBtb250aHMgYXMgdHJhaW5pbmcgZGF0YSBhbmQgcHJlZGljdCBVYmVyIHBpY2t1cHMgZm9yIEp1bmUgMjAxNSBpbiB0aGUgRWFzdCBWaWxsYWdlLiBUaGUgZGF0ZSBpcyBpbiBwb3NpdHggZm9ybWF0IHdoaWNoIGNhbiBiZSBwYXJzZWQgdXNpbmcgdGhlIGx1YnJpZGF0ZSBwYWNrYWdlLiAKClN0YXRpc3RpY2FsIGZvcmVjYXN0aW5nIHRlY2huaXF1ZXMgYXJlIHVzZWQgb24gdGhlIEVhc3QgVmlsbGFnZSB0cmFpbmluZyBkYXRhLCBhbmQgd2lsbCBiZSBldmFsdWF0ZWQgb24gRWFzdCBWaWxsYWdlIHRlc3QgZGF0YS4gCgoKYGBge3J9CiMgVGhpcyBibG9jayBzZWxlY3RzIG9ubHkgZWFzdCB2aWxsYWdlIGRhdGEgYW5kIHNwbGl0cyBpdCBpbnRvIHRyYWluL3Rlc3Qgc2V0cwpmdWxsX3RzIDwtIG1zdHModWJlcl82aCRFYXN0X1ZpbGxhZ2UsCiAgICAgICAgICAgICAgICAgICAgc3RhcnQ9ZGVjaW1hbF9kYXRlKHltZF9obXMoIjIwMTUtMDEtMDEgMDA6MDA6MDAiKSksCiAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwucGVyaW9kcz1jKDQsIDE0NjEpKQoKdHJhaW5fdHMgPC0gbXN0cyh1YmVyX3RyYWluJEVhc3RfVmlsbGFnZSwKICAgICAgICAgICAgICAgICAgICBzdGFydD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMS0wMSAwMDowMDowMCIpKSwKICAgICAgICAgICAgICAgICAgICBzZWFzb25hbC5wZXJpb2RzPWMoNCwgMTQ2MSkpCgp0ZXN0X3RzIDwtIG1zdHModWJlcl90ZXN0JEVhc3RfVmlsbGFnZSwKICAgICAgICAgICAgICAgICAgICBzdGFydD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wNi0wMSAwMDowMDowMCIpKSwKICAgICAgICAgICAgICAgICAgICBzZWFzb25hbC5wZXJpb2RzPWMoNCwgMTQ2MSkpCgoKI3RyYWluX3RzIDwtIHdpbmRvdyhmdWxsX3RzLCBzdGFydD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMS0wMSAwMDowMDowMCIpKSkgIyBUcmFpbiB3aXRoIG9ic2VyY2F0aW9ucyB1cCB1bnRpbCBqdW5lCgojdGVzdF90cyA8LSB3aW5kb3coZnVsbF90cywgc3RhcnQ9ZGVjaW1hbF9kYXRlKHltZF9obXMoIjIwMTUtMDYtMDEgMDA6MDA6MDAiKSkpICMgVGVzdCBvdXQgcHJlZGljdGlvbnMgd2l0aCBKdW5lIGRhdGEKeV90ZXN0IDwtIGFzLm51bWVyaWModGVzdF90cykKdGVzdF9zaXplIDwtIGxlbmd0aCh5X3Rlc3QpCmBgYAoKCmBgYHtyfQojIFNob3cgZnVsbCBkYXRhIHNldApwMSA8LSBmdWxsX3RzICU+JSBhdXRvcGxvdChzZXJpZXM9IkFsbCBUcmlwcyIpICsgCiAgZ2d0aXRsZSgiRnVsbCBEYXRhc2V0IikgKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKCJEYXRhIikpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siKSkKCiMgU2hvdyB0cmFpbmluZyBkYXRhc2V0CnAyIDwtIGF1dG9wbG90KHRyYWluX3RzLCBzZXJpZXM9IlRyYWluaW5nIikrCiAgYXV0b2xheWVyKHRlc3RfdHMsICBzZXJpZXM9IlZhbGlkYXRpb24iKSsKICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCgiU3BsaXQiKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsICJncmV5IikpKwogIGdndGl0bGUoIlRyYWluL1Rlc3QgU3BsaXQiKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0yLCBuY29sPTEpCmBgYAoKVG8gbG9vayBhdCBhbnkgb2YgdGhlIG90aGVyIDcgcGlja3VwIGxvY2F0aW9ucywgY2FsbCBgbGlsX2l0YWx5IDwtIHViZXJfdHJhaW4gJT4lIHNlbGVjdChMaXR0bGVfSXRhbHkpYCBmb3IgZXhhbXBsZS4KCmBgYHtyfQojIFNob3cgc3Vic2V0IG9mIGRhdGEgdG8gbG9vayBmb3IgcGF0dGVybnMKCmphbl90b19tYXJjaCA8LSB0cmFpbl90cyAlPiUgd2luZG93KGVuZD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMy0wMSAwMDowMDowMCIpKSkKCmphbl90b19tYXJjaCAlPiUgYXV0b3Bsb3QoKSsKICBnZ3RpdGxlKCIyIE1vbnRocyBvZiBVYmVyIFBpY2t1cCBEYXRhIikKYGBgCgoKQnkgYWdncmVnYXRpbmcgdG8gNi1ob3VyIHdpbmRvd3Mgd2UgbW9kZWwgdGhlIGRlbWFuZCBwZXIgInNoaWZ0IiBpbiBhIGRheS4KCi0gMDA6MDAgLSAwNTo1OSAtIEdyYXZleWFyZCBTaGlmdAotIDA2OjAwIC0gMTE6NTkgLSBNb3JuaW5nIFNoaWZ0Ci0gMTI6MDAgLSAxNzo1OSAtIEFmdGVybm9vbiBTaGlmdAotIDE4OjAwIC0gMjM6NTkgLSBFdmVuaW5nIFNoaWZ0CgojIyBNb3ZpbmcgQXZlcmFnZSBGaWx0ZXIKCmBgYHtyfQpqYW4gPC0gZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZGVjaW1hbF9kYXRlKHltZF9obXMoIjIwMTUtMDEtMDEgMDA6MDA6MDAiKSksIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJibHVlIiwgc2l6ZT0wLjUpCmZlYiA8LSBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBkZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMi0wMSAwMDowMDowMCIpKSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gImJsdWUiLCBzaXplPTAuNSkKbWFyIDwtIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGRlY2ltYWxfZGF0ZSh5bWRfaG1zKCIyMDE1LTAzLTAxIDAwOjAwOjAwIikpLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAiYmx1ZSIsIHNpemU9MC41KQphcHIgPC0gZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZGVjaW1hbF9kYXRlKHltZF9obXMoIjIwMTUtMDQtMDEgMDA6MDA6MDAiKSksIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJibHVlIiwgc2l6ZT0wLjUpCm1heSA8LSBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBkZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wNS0wMSAwMDowMDowMCIpKSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gImJsdWUiLCBzaXplPTAuNSkKanVuIDwtIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGRlY2ltYWxfZGF0ZSh5bWRfaG1zKCIyMDE1LTA2LTAxIDAwOjAwOjAwIikpLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAiYmx1ZSIsIHNpemU9MC41KQoKdHJhaW5fdHMgJT4lIGF1dG9wbG90KHNlcmllcz0iT3JpZ2luYWwgRGF0YSIpKwogIGF1dG9sYXllcihtYSh0cmFpbl90cywgNCksIHNlcmllcz0iTW92aW5nIEF2ZXJhZ2UiKSsKICBqYW4gKyBmZWIgKyBtYXIgKyBhcHIgKyBtYXkgKyBqdW4gKyAjIGFkZGluZyBtb250aCBsaW5lcwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKCJTcGxpdCIpKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwgImdyZXkiKSkrCiAgZ2d0aXRsZSgiTW92aW5nIEF2ZXJhZ2Ugb2YgVHJhaW5pbmcgRGF0YSIpCgpgYGAKClRoaXMgaGVscHMgd2l0aCB0ZWxsaW5nIHRoZSBzZWFzb25hbGl0eSBvZiB0aGUgZGF0YS4gSXQgbG9va3MgbGlrZSB0aGVyZSBpcyBhIHNwaWtlIGluIGRlbWFuZCBhdCB0aGUgYmVnaW5uaW5nIG9mIGVhY2ggbW9udGguIEluIGFkZGl0aW9uIHRoZXJlIGFyZSBhYm91dCB0aHJlZSBzcGlrZXMgZHVyaW5nIGVhY2ggbW9udGguIFRoaXMgcHJvYmFibHkgY29ycmVzcG9uZHMgdG8gX193ZWVrbHkgc3Bpa2VzIGluIGRlbWFuZF9fLiAKCiMjIEFuYWx5emluZyB0aGUgdHJlbmQKCmBgYHtyfQpsbSh0cmFpbl90cyB+IHRpbWUodHJhaW5fdHMpKSAlPiUgZml0dGVkKCkgLT4geWhhdC5sbQphdXRvcGxvdCh0cmFpbl90cykgKyAKICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD10aW1lKHRyYWluX3RzKSwgeT15aGF0LmxtKSwgY29sb3I9InJlZCIpKwogIGdndGl0bGUoIkxpbmVhciBUcmVuZCBGaXQiKSsKICB4bGFiKCJUaW1lIikrCiAgeWxhYigiUGlja3VwcyIpCmBgYAoKClRoZXJlIGlzIGEgc2xpZ2h0IGxpbmVhciB0cmVuZC4gTGV0J3Mgc2VlIHdoYXQgdGhlIEFDRiBhbmQgUEFDRiBwbG90cyBsb29rIGxpa2UuCgoKYGBge3J9CnAxIDwtIGdnQWNmKHRyYWluX3RzKSArIGdndGl0bGUoIkFDRiBvZiBVYmVyIFBpY2t1cHMiKQpwMiA8LSBnZ1BhY2YodHJhaW5fdHMpICsgZ2d0aXRsZSgiUEFDRiBvZiBVYmVyIFBpY2t1cHMiKQpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93PTIsIG5jb2w9MSkKYGBgCgoKCk5vdCB2ZXJ5IGluZm9ybWF0aXZlIGFsdGhvdWdoIGl0IGluaXRpYWxseSBoaW50cyB0b3dhcmQgYW4gQVIocCkgbW9kZWwgYWZ0ZXIgYSBkaWZmZXJlbmNlLiAKCkJlZm9yZSBjaGVja2luZyB0aGUgbGFnIHBsb3QsIHRoZSB0cmVuZCBzaG91bGQgYmUgcmVtb3ZlZCB2aWEgZGlmZmVyZW5jaW5nLgoKIyMgRGlmZmVyZW5jaW5nIAoKYGBge3J9CiMgY29kZSBmb3IgZGlmZmVyZW5jaW5nIHRoZSB0cmFpbmluZyBkYXRhCnRyYWluX2RpZmYgPC0gZGlmZih0cmFpbl90cykKbG0odHJhaW5fZGlmZiB+IHRpbWUodHJhaW5fZGlmZikpICU+JSBmaXR0ZWQoKSAtPiB5aGF0LmxtCgp0cmFpbl9kaWZmICU+JSBhdXRvcGxvdCgpICsgCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0cmFpbl9kaWZmKSwgeT15aGF0LmxtKSwgY29sb3I9InJlZCIpICsgCiAgZ2d0aXRsZSgiRGlmZmVyZW5jZWQgUGlja3VwIERhdGEiKSArIAogIHlsYWIoIkNoYW5nZSBpbiBQaWNrdXBzIikKYGBgCgpgYGB7cn0KIyBDb21wYXJpc29uIG9mIHRpbWUgc2VyaWVzIHBsb3RzCgpsbSh0cmFpbl90cyB+IHRpbWUodHJhaW5fdHMpKSAlPiUgZml0dGVkKCkgLT4geWhhdC5sbTEKcDEgPC0gYXV0b3Bsb3QodHJhaW5fdHMpICsgCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0cmFpbl90cyksIHk9eWhhdC5sbTEpLCBjb2xvcj0icmVkIikrCiAgZ2d0aXRsZSgiT3JpZ2luYWwgRGF0YSB3aXRoIExpbmVhciBGaXQiKSsKICB4bGFiKCJUaW1lIikrCiAgeWxhYigiUGlja3VwcyIpCgojIGNvZGUgZm9yIGRpZmZlcmVuY2luZyB0aGUgdHJhaW5pbmcgZGF0YQp0cmFpbl9kaWZmIDwtIGRpZmYodHJhaW5fdHMpCmxtKHRyYWluX2RpZmYgfiB0aW1lKHRyYWluX2RpZmYpKSAlPiUgZml0dGVkKCkgLT4geWhhdC5sbTIKCnAyIDwtIHRyYWluX2RpZmYgJT4lIGF1dG9wbG90KCkgKyAKICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD10aW1lKHRyYWluX2RpZmYpLCB5PXloYXQubG0yKSwgY29sb3I9InJlZCIpICsgCiAgZ2d0aXRsZSgiRGlmZmVyZW5jZWQgRGF0YSB3aXRoIExpbmVhciBGaXQiKSArIAogIHlsYWIoIkNoYW5nZSBpbiBQaWNrdXBzIikKCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3c9MSwgbmNvbD0yKQpgYGAKCgoKIyMgWm9vbSBpbgoKYGBge3J9CmRpZmZ3aW4gPC0gdHJhaW5fZGlmZiAlPiUgd2luZG93KGVuZD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMy0wMSAwMDowMDowMCIpKSkgCmRpZmZ3aW4gJT4lIGF1dG9wbG90KCkgKyBnZ3RpdGxlKCIyIE1vbnRocyBvZiBEaWZmZXJlbmNlZCBQaWNrdXBzIikKYGBgCgpgYGB7cn0KCmRpZmZ3aW4gJT4lIGF1dG9wbG90KHNlcmllcz0iT3JpZ2luYWwgRGF0YSIpKwogIGF1dG9sYXllcihtYShkaWZmd2luLCAyOCksIHNlcmllcz0iTW92aW5nIEF2ZXJhZ2UiKSsKICBqYW4gKyBmZWIgKyBtYXIgKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKCJTcGxpdCIpKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwgImdyZXkiKSkrCiAgZ2d0aXRsZSgiTW92aW5nIEF2ZXJhZ2Ugb2YgVHJhaW5pbmcgRGF0YSAoSmFuIGFuZCBGZWIpIikKYGBgCgpgYGB7cn0KcDEgPC0gdHJhaW5fZGlmZiAgJT4lIGdnQWNmKCkgKyBnZ3RpdGxlKCJBQ0Ygb2YgRGlmZmVyZW5jZWQgVWJlciBQaWNrdXBzIikKcDIgPC0gdHJhaW5fZGlmZiAlPiUgZ2dQYWNmKCkgKyBnZ3RpdGxlKCJQQUNGIG9mIERpZmZlcmVuY2VkIFViZXIgUGlja3VwcyIpCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3c9MSwgbmNvbD0yKQpgYGAKCgpgYGB7cn0KIyBBQ0Ygb2YgZGlmZmVyZW5jZWQgZGF0YQp0cmFpbl9kaWZmICU+JSBBY2YocGxvdD1GQUxTRSwgbGFnLm1heD0zMCkKYGBgCgpgYGB7cn0KIyBQQUNGIG9mIGRpZmZlcmVuY2VkIGRhdGEKdHJhaW5fZGlmZiAlPiUgUGFjZihwbG90PUZBTFNFLCBsYWcubWF4PTMwKQpgYGAKCgpUaGVyZSBpcyBjbGVhciBzZWFzb25hbGl0eSBhbmQgYSBzaGFycCBjdXRvZmYgYWZ0ZXIgbGFnIDI2LWlzaC4gRWFjaCBsYWcgaXMgMSBzaGlmdCwgc28gMjQgc2hpZnRzIHdvdWxkIGJlIDYgZGF5cyBvZiBwaWNrdXBzLCAyNiBzaGlmdHMgd291bGQgYmUgNi41IGRheXMgb2YgcGlja3VwcyAoZXZlbmluZyBzaGlmdCBvZiB0aGUgNnRoIGRheSkuIAoKSW4gdGhlIEFDRiBwbG90IHRoZXJlIGlzIGEgc3Bpa2UgYXQgbGFnIDQsIGFuZCBhbm90aGVyIGxvd2VyIHNwaWtlIGF0IGxhZyA4LCBsYWcgMTIsIGV0Yy4gSW4gYWRkaXRpb24sIHRoZXJlIGFyZSBsYXJnZSBzcGlrZXMgYXQgbGFnIDI4bi4gVGhpcyBpbmRpY2F0ZXMgYW4gQVIoNCkgY29tcG9uZW50IGFuZCBzZWFzb25hbCBjb21wb25lbnQgYXQgbGFnIDI4LiBUaGUgUEFDRiBzcGlrZXMgYW5kIGN1dHMgb2ZmIGFmdGVyIGxhZyAzLCBhbmQgc3Bpa2VzIGFnYWluIGF0IGxhZyAyOC4gVGhpcyBmdXJ0aGVyIHN1Z2dlc3RzIGFuIEFSKDQpIG9yIEFSKDMpIG1vZGVsLgoKYGBge3J9CmdnbGFncGxvdCh0cmFpbl9kaWZmLCBzZXQubGFncz0gYyg0LCA4LCAxMiwgMjgsIDU2LCA4NCksIGRvLmxpbmVzPUZBTFNFLCBjb2xvdXI9RkFMU0UpICsgZ2d0aXRsZSgiQXV0b2NvcnJlbGF0aW9uIGZvciBkaWZmZXJlbnQgbGFnczogVHJhaW4gRGlmZiIpCmBgYAoKV2UgbG9va2VkIGF0IHRoZSBsYWdzIGZvciB0aGUgZGlmZmVyZW5jZWQgdGltZSBzZXJpZXMuIFdlIHN1c3BlY3QgdGhhdCB0aGVyZSBpcyBhIHBvc2l0aXZlIGF1dG9jb3JyZWxhdGlvbiBhdCBsYWcgNG4gYmVjYXVzZSBvZiBhc3N1bWVkIGRhaWx5IHNlYXNvbmFsaXR5LiBUaGUgb3RoZXIgcGxhY2UgdG8gY2hlY2sgd291bGQgYmUgd2hlcmUgdGhlIEFDRidzIHNlYXNvbmFsIGRlY2F5IGV4aGliaXRlZCBzcGlrZXMgYXQgbGFnIDI4bi4gTG9va2luZyBhdCB0aGUgcGxvdCBhYm92ZSBpdCdzIGNsZWFyIHRoYXQgdGhpcyBpcyBhIGhpZ2hseSBwcmVkaWN0aXZlIGxhZy4KCk5leHQgd2UgZGlmZmVyZW5jZSB0aGUgc2VyaWVzIFM9MjggdGltZXMgZm9yIHRoZSBEPTEgc2Vhc29uYWwgZGlmZmVyZW5jZS4gSGVyZSB3ZSBjYW4gc2VlIHdoYXQgdGhlIG9yZGVyIG9mIHRoZSBzZWFzb25hbCBjb21wb25lbnQgd291bGQgYmUuCgpgYGB7cn0KdHJhaW5fc2RpZmYgPC0gdHJhaW5fdHMgJT4lIGRpZmYoKSAlPiUgZGlmZihsYWc9MjgpCgpwMSA8LSB0cmFpbl9zZGlmZiAlPiUgZ2dBY2YoKSArIGdndGl0bGUoIkFDRiBvZiBEaWZmKDI4KSBkYXRhIikKcDIgPC0gdHJhaW5fc2RpZmYgJT4lIGdnUGFjZigpICsgZ2d0aXRsZSgiUEFDRiBvZiBEaWZmKDI4KSBkYXRhIikKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0yLCBuY29sPTEpCmBgYAoKCmBgYHtyfQojIEFDRiBvZiBzZWFzb25hbCBjb21wb25lbnQKdHJhaW5fdHMgJT4lIGRpZmYoKSAlPiUgZGlmZihsYWc9MjgpICU+JSBBY2YocGxvdD1GQUxTRSwgbGFnLm1heD0zMCkgCmBgYAoKYGBge3J9CiMgUEFDRiBvZiBzZWFzb25hbCBjb21wb25lbnQKdHJhaW5fdHMgJT4lIGRpZmYoKSAlPiUgZGlmZihsYWc9MjgpICU+JSBQYWNmKHBsb3Q9RkFMU0UsIGxhZy5tYXg9MzApIApgYGAKClRoZSBBQ0YgYW5kIFBBQ0Ygb2YgdGhlIHNlYXNvbmFsIGRpZmZlcmVuY2VkIGRhdGEgbG9vayB2ZXJ5IHNpbWlsYXIuIFRoZXJlIGlzIGEgc3Bpa2UgYXQgbGFnIDEgYW5kIGF0IGxhZyAyOCAoQUNGKSBhbmQgbGFnIDI5IChQQUNGKS4gQmVzaWRlcyB0aGVzZSB0d28gc3Bpa2VzLCB0aGUgcmVzdCBvZiB0aGUgdmFsdWVzIHRlbmQgdG8gc3RpY2sgYXJvdW5kIHplcm8uIFRoaXMgaXMgaW5kaWNhdGl2ZSBvZiBhbiBTTUEoMSkgc2Vhc29uYWwgY29tcG9uZW50LiBUaGVyZSBkb2Vzbid0IGFwcGVhciB0byBiZSBhbnkgU0FSKFApIGNvbXBvbmVudC4gU2luY2Ugd2UgdG9vayBvbmUgc2Vhc29uYWwgZGlmZmVyZW5jZSwgdGhlbiBEPTEuCgpXZSB3aWxsIG5vdyBfX21ha2UgYW4gQVJJTUEgbW9kZWwgYmFzZWQgb24gdGhlIEFDRiBhbmQgUEFDRiBwbG90cyBvZiB0aGUgZGlmZigxKSBhbmQgZGlmZigyOClfXyB2ZXJzaW9uIG9mIG91ciBvcmlnaW5hbCBkYXRhLiAKCgojIyBTQVJJTUEgTW9kZWxzCgoKVGhlIGZpcnN0IG1vZGVsIGlzIGJhc2VkIG9uIHRoZSBtb2RlbCBwcmVzZW50ZWQgaW4gdGhlIHRpbWUgc2VyaWVzIGFuYWx5c2lzIGJvb2sgbWl4ZWQgd2l0aCB0aGUgc2Vhc29uYWxpdHkgdGhhdCB3ZSBvYnNlcnZlIGluIHRoZSBVYmVyIGRhdGFzZXQuIFRoaXMgaXMgYSBtb2RlbCB0aGF0IGlzIHVzZWQgaW4gZWNvbm9taWNzIGFuZCB3YXMgdXNlZCB0byBwcmVkaWN0IGFpcmxpbmUgcGFzc2VuZ2VycyBpbiB0aGUgYm9vay4gCgpgYGB7cn0KIyBJbnR1aXRpdmUgbW9kZWwgYmFzZWQgb24gdGhlIHBsb3RzCm0xIDwtIEFyaW1hKHRyYWluX3RzLCAKICAgICAgb3JkZXI9YygxLCAxLCAwKSwgCiAgICAgIHNlYXNvbmFsPWxpc3Qob3JkZXI9YygwLDEsMSksIHBlcmlvZD0yOCkpCgp5aGF0IDwtIG0xICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkKCiMgUGxvdCB0aGUgcHJlZGljdGlvbnMKcDEgPC0geWhhdCAlPiUgYXV0b3Bsb3QoKSAKcDIgPC0gZ2dBY2YocmVzaWR1YWxzKG0xKSkgKyBnZ3RpdGxlKCJBQ0Ygb2YgbTEgUmVzaWR1YWxzIikKCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3c9MSwgbmNvbD0yKQoKIyBQcmludCBvdXQgc3VtbWFyeSBvZiB0aGUgY29lZmZpY2llbnRzCm0xICU+JSBzdW1tYXJ5KCkKCm0xX3kgPC0gbTEgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQpgYGAKClRoZSBwYXR0ZXJuIGxvb2tzIGxpa2UgaXQgd2FzIGNhcHR1cmVkIGluIHRoZSBwcmVkaWN0aXZlIG1lYW4gb2YgdGhlIGZvcmVjYXN0LCBidXQgdGhlIHByZWRpY3Rpb24gaW50ZXJ2YWxzIGdldCByZWFsbHkgd2lkZSBmb3IgZm9yZWNhc3RzIGJleW9uZCBhYm91dCAyIHdlZWtzLiBBdCB0aGlzIHBvaW50LCB0aGUgcHJlZGljdGlvbiBpbnRlcnZhbCBjYXB0dXJlcyBuZWdhdGl2ZSB2YWx1ZXMuIAoKTmV4dCB3ZSB1c2UgcD00IHNpbmNlIHRoaXMgaXMgd2hhdCB3YXMgdW5jb3ZlcmVkIGluIHRoZSBBQ0YgYW5kIFBBQ0YgcGxvdHMgb2YgdGhlIGRpZmZlcmVuY2VkIHRpbWUgc2VyaWVzLgoKCmBgYHtyfQptMiA8LSBBcmltYSh0cmFpbl90cywgCiAgICAgIG9yZGVyPWMoNCwgMSwgMCksIAogICAgICBzZWFzb25hbD1saXN0KG9yZGVyPWMoMCwxLDEpLCBwZXJpb2Q9MjgpKQoKCm0yX3kgPC0gbTIgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQoKcDEgPC0geWhhdDIgJT4lIGF1dG9wbG90KCkKcDIgPC0gZ2dBY2YocmVzaWR1YWxzKG0yKSkgKyBnZ3RpdGxlKCJBQ0Ygb2YgUmVzaWR1YWxzIikKCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3c9MSwgbmNvbD0yKQpgYGAKClRoaXMgc2Vjb25kIG1vZGVsIGRpcHMgYmVsb3cgemVybyBpbiB0aGUgcHJlZGljdGlvbiBpbnRlcnZhbCBhZnRlciB0aGUgZmlyc3Qgd2Vlay4gVGhlIEFJQyBhbmQgQklDIG9mIHRoaXMgbW9kZWwgYXJlIGFsc28gbG93ZXIgdGhhbiB0aGF0IG9mIHRoZSBmaXJzdCBlY29ub21pYyBtb2RlbC4KClRoZSBsYXN0IG1vZGVsIGlzIG9uZSB0aGF0IGlzIGNob3NlbiBieSBgYXV0by5hcmltYWAgb24gdGhlIGJhc2lzIG9mIEFJQy4gSG93ZXZlciwgdGhlIGZ1bmN0aW9uIG5lZWRzIGEgdGltZSBzZXJpZXMgd2l0aCBmcmVxdWVuY3kgZGVmaW5lZCBhbmQgSSBzdHJ1Z2dsZWQgdG8gdW5kZXJzdGFuZCB0aGUgZnJlcXVlbmN5IHBhcmFtZXRlci4gSSBtYWRlIGEgZ3Vlc3MgdGhhdCBpdCB3b3VsZCBiZSB0aGUgbnVtYmVyIG9mIGRheXMgaW4gYSB5ZWFyICgxIHNlYXNvbikgZGl2aWRlZCBieSB0aGUgdmFsdWUgb2YgdGhlIHNlYXNvbiBwcmVzZW50IGluIG15IGRhdGEuCgpgYGB7cn0KIyBBdXRvIGFyaW1hIHdpdGggZm9yY2VkIHNlYXNvbmFsaXR5CnRyYWluX3NlYXNvbmFsIDwtIHRzKHViZXJfdHJhaW4kRWFzdF9WaWxsYWdlLCBmcmVxdWVuY3k9MzY1LjQvMjgpCm1fYXV0byA8LSBhdXRvLmFyaW1hKHRyYWluX3NlYXNvbmFsLCBEPTEpIAptX2F1dG8gJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKSAlPiUgYXV0b3Bsb3QoKQptX2F1dG9feSA8LSBtX2F1dG8gJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQpgYGAKCmBgYHtyfQptX2F1dG8gPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDMsIDAsIDIpLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDIsMSwwKSwgcGVyaW9kPTEzKSkKCm1fYXV0b195IDwtIG1fYXV0byAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCmBgYAoKYGBge3J9CnNmaXQgPC0gc25haXZlKHRyYWluX3RzKSAKeWhhdF9zZml0IDwtIHNmaXQgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQpzZml0ICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkgJT4lIGF1dG9wbG90KCkKYGBgCgoKClRoaXMgbW9kZWwgZG9lcyB3b3JzZSB0aGFuIHRoZSBmaXJzdCB0d28uIAoKYGBge3J9CiMgQ29tcGFyaXNvbiBvZiBtb2RlbCByZXN1bHRzCnAxIDwtIGdncGxvdCgpKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD15X3Rlc3QsIHk9eWhhdCRtZWFuKSkrCiAgeGxpbShtaW4oeV90ZXN0KSwgbWF4KHlfdGVzdCkpKwogIHlsaW0obWluKHlfdGVzdCksIG1heCh5X3Rlc3QpKSsKICBnZ3RpdGxlKCJBY3R1YWwgdnMuIFByZWRpY3RlZCAobTEpIikKCnAyIDwtIGdncGxvdCgpKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD15X3Rlc3QsIHk9eWhhdDIkbWVhbikpKwogIHhsaW0obWluKHlfdGVzdCksIG1heCh5X3Rlc3QpKSsKICB5bGltKG1pbih5X3Rlc3QpLCBtYXgoeV90ZXN0KSkrCiAgZ2d0aXRsZSgiQWN0dWFsIHZzLiBQcmVkaWN0ZWQgKG0yKSIpCgpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93PTEsIG5jb2w9MikKYGBgCgoKYGBge3J9Cm03IDwtIEFyaW1hKHRyYWluX3RzLCAKICAgICAgb3JkZXI9Yyg0LCAxLCAwKSwgCiAgICAgIHNlYXNvbmFsPWxpc3Qob3JkZXI9YygwLDEsNCksIHBlcmlvZD0yOCkpCgptN195IDwtIG03ICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkKCnAxIDwtIHloYXQ3ICU+JSBhdXRvcGxvdCgpCnAyIDwtIGdnQWNmKHJlc2lkdWFscyhtNykpICsgZ2d0aXRsZSgiQUNGIG9mIFJlc2lkdWFscyIpCgpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93PTEsIG5jb2w9MikKYGBgCgpgYGB7cn0KQklDKG0yKQpCSUMobTcpCmBgYAoKYGBge3J9Cm04IDwtIEFyaW1hKHRyYWluX3RzLCAKICAgICAgb3JkZXI9YygzLCAxLCAwKSwgCiAgICAgIHNlYXNvbmFsPWxpc3Qob3JkZXI9YygwLDEsMSksIHBlcmlvZD0yOCkpCgp5aGF0OCA8LSBtOCAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCgpwMSA8LSB5aGF0OCAlPiUgYXV0b3Bsb3QoKQpwMiA8LSBnZ0FjZihyZXNpZHVhbHMobTgpKSArIGdndGl0bGUoIkFDRiBvZiBSZXNpZHVhbHMiKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0xLCBuY29sPTIpCmBgYAoKIyMjIExqdW5nLUJveCBUZXN0cwoKYGBge3J9CiMgTGp1bmcgQm94IHRlc3RzCmZpdF90ZXN0IDwtIHNhcmltYSh0cmFpbl90cywgcD00LCBkPTEsIHE9MCwgUD0wLCBEPTEsICBRPTEsIFM9MjgpCgpmaXRfdGVzdApgYGAKCmBtMWAgd2FzIHByZXR0eSBnb29kIGJ1dCBkaWRuJ3QgcGFzcyB0aGUgTGp1bmctQm94IHRlc3Qgb2Ygc2lnbmlmaWNhbmNlLiBIb3dldmVyIGBtMmAgZGlkLiBBbmQgdGhpcyBhbHNvIGFzc3VtZXMgbm9ybWFsbHktZGlzdHJpYnV0ZWQgZXJyb3JzIHdoaWNoIGlzbid0IGV4YWN0bHkgdHJ1ZSB3aXRoIG91ciBkYXRhIHNpbmNlIGl0J3MgUG9pc3NvbiBkaXN0cmlidXRlZC4gCgoKIyMgUG9pc3NvbiBNb2RlbHMKCmBgYHtyfQojIE9uZS1ob3QgZW5jb2RpbmcgZnVuY3Rpb24gZm9yIHdlZWtseSBzZWFzb25hbCB2YXJpYWJsZQpkbXkgPC0gZHVtbXlWYXJzKH5zZWFzb24sIGRhdGEgPSB1YmVyX3RyYWluKQp0cmFpbkNvdmFyaWF0ZXMgPC0gZGF0YS5mcmFtZShwcmVkaWN0KGRteSAsbmV3ZGF0YT1kYXRhLmZyYW1lKHNlYXNvbj11YmVyX3RyYWluJHNlYXNvbikpKQp0ZXN0Q292YXJpYXRlcyA8LSBkYXRhLmZyYW1lKHByZWRpY3QoZG15ICxuZXdkYXRhPWRhdGEuZnJhbWUoc2Vhc29uPXViZXJfdGVzdCRzZWFzb24pKSkKCiMgCm0zIDwtIHRzZ2xtKHRyYWluX3RzLCBtb2RlbD1saXN0KHBhc3Rfb2JzPTEsIHBhc3RfbWVhbj0xKSwgZGlzdHI9InBvaXNzb24iLCB4cmVnPXRyYWluQ292YXJpYXRlcykKCnloYXRfbTMgPC0gcHJlZGljdChtMywgbi5haGVhZD10ZXN0X3NpemUsIG5ld3hyZWc9dGVzdENvdmFyaWF0ZXMpCgpwcmVkaW50X20zIDwtIGRhdGEuZnJhbWUoeWhhdF9tMyRpbnRlcnZhbCkKCmdncGxvdCgpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodHJhaW5fdHMpLCB5PXRyYWluX3RzKSkrCiAgZ2VvbV9yaWJib24obWFwcGluZz1hZXMoeD10aW1lKHRlc3RfdHMpLCB5bWluPXByZWRpbnRfbTMkbG93ZXIsIHltYXggPSBwcmVkaW50X20zJHVwcGVyKSwgZmlsbD0iYmx1ZSIsIGFscGhhPTAuNSkrCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeT15aGF0X20zJG1lZGlhbiksIGNvbG9yPSJibHVlIikrCiAgZ2d0aXRsZSgiUG9pc3NvbiBHTE0gd2l0aCBBUk1BKDEsMSkgYW5kIFdlZWtkYXkgRHVtbXkgVmFyaWFibGUiKQpgYGAKCmBgYHtyfQptNCA8LSB0c2dsbSh0cmFpbl90cywgbW9kZWw9bGlzdChwYXN0X29icz0xLCBwYXN0X21lYW49MSksIGRpc3RyPSJwb2lzc29uIikKCnloYXRfbTQgPC0gcHJlZGljdChtNCwgbi5haGVhZD10ZXN0X3NpemUpCgpwcmVkaW50X200IDwtIGRhdGEuZnJhbWUoeWhhdF9tNCRpbnRlcnZhbCkKCmdncGxvdCgpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodHJhaW5fdHMpLCB5PXRyYWluX3RzKSkrCiAgZ2VvbV9yaWJib24obWFwcGluZz1hZXMoeD10aW1lKHRlc3RfdHMpLCB5bWluPXByZWRpbnRfbTQkbG93ZXIsIHltYXggPSBwcmVkaW50X200JHVwcGVyKSwgZmlsbD0iYmx1ZSIsIGFscGhhPTAuNSkrCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeT15aGF0X200JG1lZGlhbiksIGNvbG9yPSJibHVlIikrCiAgZ2d0aXRsZSgiUG9pc3NvbiBHTE0gd2l0aCBBUk1BKDEsMSkiKQpgYGAKCmBgYHtyfQoKbTUgPC0gdHNnbG0odHJhaW5fdHMsIG1vZGVsPWxpc3QocGFzdF9vYnM9MjgsIHBhc3RfbWVhbj0xKSwgZGlzdHI9InBvaXNzb24iKQoKeWhhdF9tNSA8LSBwcmVkaWN0KG01LCBuLmFoZWFkPXRlc3Rfc2l6ZSkKCnByZWRpbnRfbTUgPC0gZGF0YS5mcmFtZSh5aGF0X201JGludGVydmFsKQoKbWVhbigoeV90ZXN0IC0geWhhdF9tNSRtZWRpYW4pXjIpCgpnZ3Bsb3QoKSsKICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD10aW1lKHRyYWluX3RzKSwgeT10cmFpbl90cykpKwogIGdlb21fcmliYm9uKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeW1pbj1wcmVkaW50X201JGxvd2VyLCB5bWF4ID0gcHJlZGludF9tNSR1cHBlciksIGZpbGw9ImJsdWUiLCBhbHBoYT0wLjUpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodGVzdF90cyksIHk9eWhhdF9tNSRtZWRpYW4pLCBjb2xvcj0iYmx1ZSIpKwogIGdndGl0bGUoIlBvaXNzb24gR0xNIHdpdGggQVJNQSgyOCwxKSIpCgptMTMgPC0gdHNnbG0odHJhaW5fdHMsIG1vZGVsPWxpc3QocGFzdF9vYnM9MjgsIHBhc3RfbWVhbj0yOCksIGRpc3RyPSJwb2lzc29uIikKCnloYXRfbTYgPC0gcHJlZGljdChtMTMsIG4uYWhlYWQ9dGVzdF9zaXplKQoKcHJlZGludF9tNiA8LSBkYXRhLmZyYW1lKHloYXRfbTYkaW50ZXJ2YWwpCgptZWFuKCh5X3Rlc3QgLSB5aGF0X202JG1lZGlhbileMikKCmdncGxvdCgpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodHJhaW5fdHMpLCB5PXRyYWluX3RzKSkrCiAgZ2VvbV9yaWJib24obWFwcGluZz1hZXMoeD10aW1lKHRlc3RfdHMpLCB5bWluPXByZWRpbnRfbTYkbG93ZXIsIHltYXggPSBwcmVkaW50X202JHVwcGVyKSwgZmlsbD0iYmx1ZSIsIGFscGhhPTAuNSkrCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeT15aGF0X202JG1lZGlhbiksIGNvbG9yPSJibHVlIikrCiAgZ2d0aXRsZSgiUG9pc3NvbiBHTE0gd2l0aCBBUk1BKDI4LDI4KSIpCmBgYAoKYGBge3J9CmRteSA8LSBkdW1teVZhcnMofnNlYXNvbiwgZGF0YSA9IHViZXJfdHJhaW4pCnRyYWluQ292YXJpYXRlcyA8LSBkYXRhLmZyYW1lKHByZWRpY3QoZG15ICxuZXdkYXRhPWRhdGEuZnJhbWUoc2Vhc29uPXViZXJfdHJhaW4kc2Vhc29uKSkpCnRlc3RDb3ZhcmlhdGVzIDwtIGRhdGEuZnJhbWUocHJlZGljdChkbXkgLG5ld2RhdGE9ZGF0YS5mcmFtZShzZWFzb249dWJlcl90ZXN0JHNlYXNvbikpKQoKbTYgPC0gdHNnbG0odHJhaW5fdHMsIG1vZGVsPWxpc3QocGFzdF9vYnM9NCwgcGFzdF9tZWFuPTEpLCBkaXN0cj0icG9pc3NvbiIsIHhyZWc9dHJhaW5Db3ZhcmlhdGVzKQpwcmVkaW50X202IDwtIGRhdGEuZnJhbWUoeWhhdF9tNiRpbnRlcnZhbCkKCnloYXRfbTYgPC0gcHJlZGljdChtNiwgbi5haGVhZD10ZXN0X3NpemUsIG5ld3hyZWc9dGVzdENvdmFyaWF0ZXMpCgpnZ3Bsb3QoKSsKICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD10aW1lKHRyYWluX3RzKSwgeT10cmFpbl90cykpKwogIGdlb21fcmliYm9uKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeW1pbj1wcmVkaW50X202JGxvd2VyLCB5bWF4ID0gcHJlZGludF9tNiR1cHBlciksIGZpbGw9ImJsdWUiLCBhbHBoYT0wLjUpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodGVzdF90cyksIHk9eWhhdF9tNiRtZWRpYW4pLCBjb2xvcj0iYmx1ZSIpKwogIGdndGl0bGUoIlBvaXNzb24gR0xNIEFSTUEoNCwxKSBhbmQgV2Vla2RheSBEdW1teSBWYXJpYWJsZSIpCmBgYAoKClRoYXQgd29ya3MsIHNob3VsZCBJIGluY2x1ZGUgdGhlIEFJQyAvIEJJQyB0YWJsZXMgdXAgaGVyZSB0b28/IEFuZCB0aGUgcmVzaWR1YWxzIHBsb3RzIGFuZCBzdHVmZj8gQWxyaWdodHksIGlmIHlvdSBjb3VsZCBqdXN0IHB1dCBhbiBleGFtcGxlIHNlY3Rpb24gd2l0aCB5b3VyIG1vZGVsIGFuZCBJIGNhbiBqdXN0IGNvcHkgdGhlIHNhbWUgZm9ybWF0LiBJIGhhdmUgcGxlbnR5IG9mIEJTIEkgY2FuIHN0aWNrIGluIGQ6CgpKayBpbSBnb25uYSBkZWxldGUgdGhpcyBzbyB3ZSBoYXZlIHNwYWNlIGhhaGEgYnV0IEknbGwgY29weSBpdCBpbiBhIHNlcGFyYXRlIHRleGN0IGZpbGUKQ291bGQgeW91IGluY2x1ZGUgYSBxdWljayB3cml0ZXVwIG9uIGl0PyBJIHdhcyBnb25uYSB3cml0ZSB1cCBzb21ldGhpbmcgcXVpY2sgYWJvdXQgU0FSSU1BLCBhbmQgV2lsbGlhbSBpcyBnb25uYSB3cml0ZSB1cCBzb21ldGhpbmcgYWJvdXQgUG9pc3Nvbi4gU291bmRzIGdvb2QsIHdvdWxkIHlvdSBiZSBkb3duIHRvIHB1dCBpdCB1cCBoZXJlPyBXZSB3ZXJlIHRyeWluZyB0byB0aGluayBvZiB3aGF0IHRvIHB1dCB1cCBpbiB0aGlzIHNlY3Rpb24gKHVubGVzcyB5b3UgaGF2ZSBhbiBpZGVhIG9mIHdoYXQgdG8gcHV0IC4gU291bmRzIGdvb2QKCgpPdXIgYW5hbHlzaXMgY29uc2lkZXJlZCB0aHJlZSBtb2RlbHM6XFwKdGhhdHMgcmlnaHQsIGFuZCBtdWx0aXZhcmlhdGUgZ2VuZXJhbGl6ZWQgYWRkaXRpdmUgbW9kZWxzLCBvciBWR0FNJ3MuU3VyZSwgaWxsIGRvIGEgd3JpdGUgdXAgaW4gdGhlIG1vZGVsIGZpdHRpbmcgc2VjdGlvbiwgaWxsIHR5cGUgb3V0IHRoZSBhY3R1YWwgbW9kZWxzIG1hdGhlbWF0aWNhbGx5IGFuZCB0cnkgdG8gZXhwbGFpbiB0aGVtLiB5ZWEgaSBjYW4gd3JpdGUgaXQgaGVyZSBpbnN0ZWFkLCBmb3IgbW9kZWwgZml0dGluZyBhbmQgdmFsaWRhdGlvbiBwdXQgcGxvdHMgb2YgeW91ciBmb3JlY2FzdCBiZXNpZGUgdGhlIHZhbGlkYXRpb24gYW5kIHRhbGsgYWJvdXQgaG93IHdlbGwgdGhleSBmaXQgdGhlIGRhdGEuIGFsc28gcHJvYmFibHkgaW5jbHVkZSB0YWJsZXMgd2l0aCB0aGUgbW9kZWxzIGFuZCBlcnJvcnMgb24gdGhlIHZhbGlkYXRpb24gc2V0LiBZZXMsIGFuZCB0aGUgbGp1bmcgYm94IHN0dWZmIHRvbywgdGhlIHByb2NlZHVyZXMgeW91IHVzZWQgdG8gY2hvb3NlIHRoZSBiZXN0IG1vZGVsIHNob3VsZCBiZSB0aGVyZSBsaWtlIHRoZSBhaWMsIGJ1dCBsZXRzIG5vdCBnbyB0b28gY3JhenkgbG9sLiBJIHRoaW5rIGF0IG1vc3Qgd2UgaW5jbHVkZSB2YWxpZGF0aW9uIGVycm9yLCBhaWMsIHJlc2lkdWFsIG9yIFEtUSBwbG90cywgYW5kIGxqdW5nIGJveCBzaG91bGQgYmUgZ29vZC4gZm9yIHN1cmUsIGFscmlnaHQgaGF2ZSBmdW4gbG9sCgpgYGB7cn0KdHJhaW5fZnJlcSA8LSB0cyh1YmVyX3RyYWluJEVhc3RfVmlsbGFnZSwgZnJlcXVlbmN5PTI4KQp0ZXN0X2ZyZXEgPC0gdHModWJlcl90ZXN0JEVhc3RfVmlsbGFnZSwgZnJlcXVlbmN5PTI4KQoKdHJhaW5fZnJlcSAlPiUgYXV0by5hcmltYSgpIC0+IG0xMAoKcDEgPC0gbTEwICU+JSBmb3JlY2FzdChoPTEyMCkgJT4lIGF1dG9wbG90KCkKcDIgPC0gbTEwICU+JSByZXNpZHVhbHMoKSAlPiUgZ2dBY2YoKSArIGdndGl0bGUoIkFDRiBvZiBSZXNpZHVhbHMiKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0xLCBuY29sPTIpCmBgYAoKYGBge3J9CmNoZWNrcmVzaWR1YWxzKG0xMCkKYGBgCgpgYGB7cn0KbmFpdmVzX20gPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDAsIDAsIDApLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDAsMSwwKSwgcGVyaW9kPTI4KSkKCnNmaXQgPC0gdHJhaW5fdHMgJT4lIHNuYWl2ZShtPTI4KSAKcDEgPC0gbmFpdmVzX20gJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKSAlPiUgYXV0b3Bsb3QoUEk9RkFMU0UpCnAyIDwtIHNmaXQgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKSAlPiUgYXV0b3Bsb3QoKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0yLCBuY29sPTEpCmBgYAoKYGBge3J9CnNmaXQgPC0gc25haXZlKHRyYWluX3RzKSAKeWhhdF9zZml0IDwtIHNmaXQgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQpzZml0ICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkgJT4lIGF1dG9wbG90KCkKYGBgCgojIyBBY2N1cmFjeSBNZWFzdXJlcwoKYGBge3J9CiMgU0FSSU1BIG1vZGVscwptMSA8LSBBcmltYSh0cmFpbl90cywgCiAgICAgIG9yZGVyPWMoMSwgMSwgMCksIAogICAgICBzZWFzb25hbD1saXN0KG9yZGVyPWMoMCwxLDEpLCBwZXJpb2Q9MjgpKQoKbTIgPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDQsIDEsIDApLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDAsMSwxKSwgcGVyaW9kPTI4KSkKCm0zIDwtIEFyaW1hKHRyYWluX3RzLCAKICAgICAgb3JkZXI9Yyg0LCAxLCAwKSwgCiAgICAgIHNlYXNvbmFsPWxpc3Qob3JkZXI9YygwLDEsNCksIHBlcmlvZD0yOCkpCgptNCA8LSBBcmltYSh0cmFpbl90cywgCiAgICAgIG9yZGVyPWMoMywgMCwgMiksIAogICAgICBzZWFzb25hbD1saXN0KG9yZGVyPWMoMiwxLDApLCBwZXJpb2Q9MTMpKQoKbTUgPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDQsIDAsIDApLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDIsMSwwKSwgcGVyaW9kPTI4KSkKCnNfbmFpdmUgPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDAsIDAsIDApLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDAsMSwwKSwgcGVyaW9kPTI4KSkKCgp5aGF0X20xIDwtIG0xICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkKeWhhdF9tMiA8LSBtMiAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCnloYXRfbTMgPC0gbTMgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQp5aGF0X200IDwtIG00ICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkKeWhhdF9tNSA8LSBtNSAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCnloYXRfbmFpdmUgPC0gc19uYWl2ZSAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCmBgYAoKCmBgYHtyfQphY2N1cmFjeSh5aGF0X20xLCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X20yLCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X20zLCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X200LCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X201LCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X25haXZlLCB0ZXN0X3RzKQpgYGAKCmBgYHtyfQojIAptc2VfdHNnbG0xIDwtIG1lYW4oKHlfdGVzdCAtIHloYXRfbTUkbWVkaWFuKV4yKQptc2VfdHNnbG0yOCA8LSBtZWFuKCh5X3Rlc3QgLSB5aGF0X202JG1lZGlhbileMikKCm1hZV90c2dsbTEgPC0gbWVhbihhYnMoeV90ZXN0IC0geWhhdF9tNSRtZWRpYW4pKQptYWVfdHNnbG0yOCA8LSBtZWFuKGFicyh5X3Rlc3QgLSB5aGF0X202JG1lZGlhbikpCgptc2VfdHNnbG0xCm1zZV90c2dsbTI4Cm1hZV90c2dsbTEKbWFlX3RzZ2xtMjgKCmBgYAoKYGBge3J9CgoKCmBgYAoK